package com.dyw.ba01;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

import java.util.Date;

/**
 * @auther Devil
 * @create 2022-01-06-20:05
 * @Aspect:是aspectj框架中的注解 作用:表示当前类是切面类
 * 切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码
 * 位置:在类定义的上面
 * s
 */
@Aspect
public class MyAspect {
    /**
     * 定义方法:实现切面功能的
     * 方法的定义要求:
     * 1.公共方法 public
     * 2.方法没有返回值
     * 3.方法的名称自定义
     * 4.方法可以有参数,也可以没有参数
     *      如果有参数,参数不是自定义的,有几个参数类型可以使用
     */

    /**
     * @Before:表示前置通知
     *      属性:value,是切入点表达式,表示切面的功能执行的位置.
     *      位置:在方法的上面
     *特点:  1.在目标方法之前先执行
     *      2.不会改变目标方法的执行结果
     *      3.不会影响目标方法的执行.
     *      execution(public void com.dyw.ba01.SomeServiceImpl.doSome(String,Integer)表示切入的位置
     */


    /**
     * 指定通知方法中的参数:JoinPoint
     * JoinPoint:业务方法,要加入切面功能的业务方法
     *      作用是:可以在通知方法中获取方法执行的信息,例如方法的名称,方法的实参.
     *      如果你的切面功能中需要用到方法的信息,就加入JoinPoint.
     *      这个JoinPoint参数的值是由框架赋予的,必须是第一个位置的参数
     */
//    @Before(value ="execution(public void com.dyw.*..doSome(String,int))")
//    @Before(value ="execution(public void *..do*(String,int))")
//    @Before(value ="execution(public void *..doSome(String,int))")
//    @Before(value ="execution(public void com.dyw.ba01.SomeServiceImpl.doSome(String,int))")
//    public void myBefore(JoinPoint jp){
//        //通过JoinPoint 获取方法的完整定义
//        System.out.println("方法的签名: "+jp.getSignature());
//        System.out.println("方法的名称: "+jp.getSignature().getName());
//
//        //获取方法的实参
//        Object[] args = jp.getArgs();
//        for (Object arg : args) {
//            System.out.println("参数: "+arg);
//        }
//        //切面执行的功能代码
//        System.out.println("前置通知切面功能:在目标方法之前输出执行时间:"+new Date());
//
//    }

    /**
     * 后置通知定义方法,方法是实现切面功能的.
     * 方法定义要求
     * 1.公共方法public
     * 2.方法没有返回值
     * 3.方法名称自定义
     * 4.方法有参数的,推荐Object,参数名自定义
     */


    /**
     * @param res
     * @AfterReturning:后置通知 属性:1.value 切入点表达式
     * 2.returning 自定义变量,表示方法的返回值的.
     * 自定义变量名必须和通知方法的形参一样
     * .    位置:在方法定义的上面
     * 特点:
     * 1在目标方法之后生效
     * 2.能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能
     * 3.可以修改返回值
     * <p>
     * <p>
     * 后置通知的执行
     * Object res = doOther();
     * myAfterReturning(res);
     * System.out.println(res);
     */
    @AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))", returning = "res")
    public void myAfterReturning(Object res) {
        //Object res:目标方法执行后的返回值,根据返回值可以做指定的操作
        System.out.println("后置通知 目标方法的返回值是" + res);
    }

    @AfterReturning(value = "execution(* *..SomeServiceImpl.doOther2(..))", returning = "res")
    public void myAfterReturning1(Object res) {
        //Object res:目标方法执行后的返回值,根据返回值可以做指定的操作
        res = (Student) res;
        Student ak = new Student("阿坤", 20);
        ((Student) res).setName("阿坤");
        ((Student) res).setAge(20);
        System.out.println("myAfterReturning1执行....");
    }


    /**
     * 环绕通知方法的定义格式
     * 1.public
     * 2.必须要有一个返回值,推荐使用Object
     * 3.方法名称自定义
     * 4.方法有参数,固定的参数ProceedingJoinPoint
     *
     */

    /**
     * @param proceedingJoinPoint
     * @return
     * @Around: 环绕通知
     * 属性:value 切入点表达式
     * 位置:在方法的定义上面
     * 特点:
     * 1.他是功能最强的通知
     * 2.在目标方法的前和后都能被调用执行
     * 3.控制目标方法是否被调用执行
     * 4.修改原来的目标方法的执行结果.影响最后调用的结果
     * <p>
     * <p>
     * 环绕通知.等同于jdk动态代理的InvocationHandler接口
     * <p>
     * 参数: ProceedingJoinPoint就等同于jdk动态代理的Method
     * 作用:执行目标方法的
     * 返回值:就是目标方法的执行结果,可以被修改
     * <p>
     * <p>
     * ProceedingJoinPoint是继承了JoinPoint 所以它具有JoinPoint的特性
     * <p>
     * 环绕通知:经常做事务,在目标方法之前开启事务,执行目标方法,在目标方法之后提交事务
     */

    @Around(value = "execution(* *..SomeServiceImpl.doFirst(..))")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        //实现环绕通知
        Object result = null;
        //继承JoinPoint的功能
        Object[] args = proceedingJoinPoint.getArgs();
        for (Object arg : args) {
            System.out.println("实参:" + arg);
        }
        System.out.println("环绕通知:在目标方法执行之前,输出时间: " + new Date());
        //1.目标方法的调用
        result = proceedingJoinPoint.proceed();//method.invoke(); Object result = doSome();
        System.out.println("环绕通知:在目标方法执行之后,提交事务");


        return result;


    }


    @Before(value = "myPointcut()")
    public void myBefore01() {
        System.out.println("前置通知");
    }

    /**
     * @Pointcut:定义和管理切入点,如果你的项目中有多个切入点表达式是重复的,可以复用. 可以使用@Pointcut
     * 属性:value 切入点表达式
     * 位置:自定义方法的上面
     * 特点:
     * 当使用@Pointcut定义在一个方法的上面,此时这方法的名称就是切入点表达式的别名.
     * 当其他的通知中,value属性就可以使用这个方法的名称,代替切入点表达式
     */

    @Pointcut(value = "execution(* *..SomeService.doSome(..))")
    public void myPointcut() {
        //无需代码 只是用该方法名代替切入点表达式
    }
}

/**
 * 环绕通知方法的定义格式
 * 1.public
 * 2.必须要有一个返回值,推荐使用Object
 * 3.方法名称自定义
 * 4.方法有参数,固定的参数ProceedingJoinPoint
 */


